#include "NPC.h"
#include "EntityCategory.h"
#include <sstream>





const sf::Vector2f messageLocation(100.f, 400.f);

NPC::NPC()
{

}

NPC::NPC(b2World* wo, WindowManager* win, float x, float y, std::string t, std::string n, std::string b, std::string g, std::vector<NPCMessage> msgs, bool tele)
{

	world = wo;
	window = win;

	teleport = tele;

	messageMode = false;
	complete = false;

	title = t;
	name = n;
	blurb = b;

	game = std::stoi(g);

	messageCount = 0;
	messageTrans = 0;
	once = false;
	numMessages = msgs.size();

	std::string nameReplace = "$NAME$";

	for (int i = 0; i < numMessages; i++)
	{
		msgs[i].setMessage(replaceSubstring(msgs[i].getMessage(), nameReplace, name));
		messages.push_back(msgs[i]);
	}



	if(!image.loadFromFile("images/npc.png"))
		abort();


	/*for (int x = 0; x < image.getSize().x; x++)
	{
		for (int y = 0; y < image.getSize().y; y++)
		{
			if(image.getPixel(x, y) == changeColour)
			{
				image.setPixel(x, y, sf::Color::Red);
			}
		}
	}*/

	image.createMaskFromColor(sf::Color(255, 0, 255));

	if(!texture.loadFromImage(image))
		abort();

	sprite = sf::Sprite(texture);
	sprite.setPosition(x, y);


	thor::FrameAnimation walk;

	addFrames(walk, 0, 0, 0);
	addFrames(walk, 1, 0, 0);
	addFrames(walk, 2, 0, 0);
	addFrames(walk, 3, 0, 0);

	

	animator.addAnimation("walk", walk, sf::seconds(0.5f));

	thor::FrameAnimation stand;

	addFrames(stand, 0, 1, 1);

	animator.addAnimation("stand", stand, sf::seconds(0.5f));

	thor::FrameAnimation jump;

	addFrames(jump, 1, 1, 4);

	animator.addAnimation("jump", jump, sf::seconds(1.0f));

	float width = sprite.getLocalBounds().width/2;
	float height = sprite.getLocalBounds().height/8;
	
	
	b2BodyDef bodyDef;
	bodyDef.position = b2Vec2((x)/window->getScale(), (y)/window->getScale());
	bodyDef.type = b2_dynamicBody;
    body = world->CreateBody(&bodyDef);

	b2PolygonShape shape;
	shape.SetAsBox((width*0.5/2)/window->getScale(), (height*0.8/2)/window->getScale()); // Creates a box shape. Divide your desired width and height by 2.
    b2FixtureDef fixtureDef;
    fixtureDef.density = 2000.f;
    fixtureDef.friction = 0.4f;
    fixtureDef.shape = &shape;
	fixtureDef.filter.categoryBits = NPCBODY;
	fixtureDef.filter.maskBits = GROUND | CHARACTER;
    body->CreateFixture(&fixtureDef);

	body->SetFixedRotation(true);

	autoTrans = false;
}


NPC::~NPC(void)
{

}

void NPC::drawNPC()
{

	if (autoTrans)
	{
		sf::Time elapsed = autoTransClock.getElapsedTime();

		if (elapsed.asSeconds() >= std::abs(messages[messageCount].getLight()))
		{
			autoTrans = false;
			nextMessage();
			
		}
	}
	

	if (messageMode)
	{
		sf::Text title(resizeMessage(messages[messageCount].getMessage()), *window->getFont());
		title.setCharacterSize(25);
		if (messageCount != messageTrans)
		{
			messages[messageCount].checkLight(window, name, messageCount);
			once = true;
		}
		else if (!once)
		{
			messages[messageCount].checkLight(window, name, messageCount);
			once = true;
		}
		messageTrans = messageCount;
		title.setColor(window->getBackground()->getTextColour());
		sf::FloatRect titleRect = title.getLocalBounds();
		sf::Vector2f center = window->getCenter();
		title.setPosition(messageLocation.x + window->getOffset(), messageLocation.y);
		window->draw(title);

		//sf::RectangleShape sprite(sf::Vector2f(width, height));
		//sprite.setFillColor(window->getBackground()->filter(sf::Color::Red));
		//sprite.setOrigin(width/2, height/2);
		//sprite.setPosition((window->getScale() * body->GetPosition().x), (window->getScale() * body->GetPosition().y));
		//sprite.setRotation(body->GetAngle() * 180/b2_pi);
		////Sprite.
		//window->draw(sprite);

		float width = sprite.getLocalBounds().width;
		float height = sprite.getLocalBounds().height;

		sprite.setPosition((window->getScale() * body->GetPosition().x) - (width/2), (window->getScale() * body->GetPosition().y) - (height/2));

		if(!animator.isPlayingAnimation())
			animator.playAnimation("walk");


		animator.update(clock.restart());
		animator.animate(sprite);

		sprite.setTexture(texture);
		sprite.setTextureRect(sf::IntRect(32, 320, 32, 64));

		window->draw(sprite);

		for (int i = 0; i < balls.size(); i++)
		{
			if (!balls.at(i)->drawBall())
			{
				balls.erase(balls.begin()+i);
			}
		}
	}
}

b2Vec2 NPC::getPosition()
{
	return b2Vec2(body->GetPosition().x*window->getScale(), body->GetPosition().y*window->getScale());
}

float NPC::getRadius()
{
	float width = sprite.getLocalBounds().width/4;
	float height = sprite.getLocalBounds().height/8;
	if (width > height)
	{
		return width;
	}
	return height;
}

void NPC::startMessages()
{
	messageMode = true;
}

void NPC::nextMessage()
{
	messageCount++;
	if (messageCount >= numMessages)
	{
		messageMode = false;
		complete = true;
		if (!teleport)
		{
			body->GetWorld()->DestroyBody(body);
		}
	}
	else if(messages[messageCount].getFight() == 99 || messages[messageCount].getFight() == 98)
	{
		monster->hit(25, false);
		autoTrans = true;
		autoTransClock.restart();
	}
	else if (messages[messageCount].getFight() == 50)
	{
		balls.push_back(std::make_shared<Ball>(window, world, getPosition(), b2Vec2(monster->getPosition().x, monster->getPosition().y)));
		monster->addAdditional(5);
	}
	else if (messages[messageCount].getFight() == 51)
	{
		balls.push_back(std::make_shared<Ball>(window, world, getPosition(), b2Vec2(monster->getPosition().x, monster->getPosition().y)));
		monster->addAdditional(85);
	}
	else if (messages[messageCount].getFight() == 52)
	{
		monster->reset();
	}
}

bool NPC::isComplete()
{
	return complete;
}

std::string NPC::resizeMessage(std::string msg)
{
	std::stringstream ss(msg);
	std::string buffer;
	std::vector<std::string> split;
	while (ss >> buffer)
	{
		split.push_back(buffer);
	}

	std::string result;
	int i = 0;

	while (i < split.size())
	{
		std::string temp = split[i];
		std::string backup = temp;
		bool canAdd = true;
		i++;
		while (canAdd && i < split.size())
		{
			temp += " " + split[i];
			sf::Text title(temp, *window->getFont());
			title.setCharacterSize(25);
			sf::FloatRect titleRect = title.getLocalBounds();
			if (messageLocation.x + titleRect.width < window->getWidth() - messageLocation.x)
			{
				backup = temp;
				i++;
			}
			else
			{
				temp = backup;
				canAdd = false;
			}
		}
		result += temp + "\n";
	}

	return result;

}



std::string NPC::replaceSubstring(std::string SMain, std::string& searchString, std::string& replaceString) 
{
  // This is because string defines the type to be returned 
  // from find. However, it is often a BIGGER type than
  // int, and find returns a value std::string::npos to means
  // no string found. which does not fit in int.
	std::string newString = SMain;
  std::string::size_type index;
  index = newString.find(searchString); 
  while(index!=std::string::npos)
    {
      newString.replace(index,searchString.size(),replaceString);
      // see if there are any more to do
      index = newString.find(searchString); 
    } 
  return newString;
}

void NPC::reset()
{
	messageMode = false;
	complete = false;
	messageCount = 0;
}

void NPC::finishNPC()
{
	messageCount = numMessages;
	nextMessage();
}

void NPC::previousMessage()
{
	if (messageCount > 0)
	{
		float prevLight = messages[messageCount-1].getLight();
		float curLight = messages[messageCount].getLight();
		if (prevLight != curLight)
		{
			if (prevLight != 0)
			{
				sf::Color target = sf::Color::White;
				if (prevLight < 0)
				{
					target = sf::Color::Black;
				}
				window->getBackground()->transition(std::abs(prevLight), target);
			}
			else if (curLight > 0)
			{
				window->getBackground()->transition(1, sf::Color::Black);
			}
			else
			{
				window->getBackground()->transition(1, sf::Color::White);
			}
		}

		messageCount--;
	}
}

std::string NPC::getTitle()
{
	return title;
}

std::string NPC::getName()
{
	return name;
}

std::string NPC::getBlurb()
{
	return blurb;
}

std::vector<NPCMessage>* NPC::getMessages()
{
	return &messages;
}


float NPC::getHeight()
{
	float height = sprite.getLocalBounds().height/8;
	return height;
}


// Adds a range of frames, assuming they are aligned as rectangles in the texture.
// animation:      FrameAnimation to modify
// x:              Column index of the texture rectangle
// [yFirst,yLast]: Bounds for row indices (if yLast < yFirst, add frames in reverse order)
// duration:       Relative duration of current frame (1 by default)
void NPC::addFrames(thor::FrameAnimation& animation, int x, int yFirst, int yLast, float duration)
{
	const int step = (yFirst < yLast) ? +1 : -1;
	yLast += step; // so yLast is excluded in the range

	for (int y = yFirst; y != yLast; y += step)
		animation.addFrame(duration, sf::IntRect(32*x, 64*y, 32, 64));
}

void NPC::addMonster(Monster* m)
{
	monster = m;
}


NPCMessage* NPC::getCurrentMessage()
{
	return &messages[messageCount];
}

bool NPC::isFight()
{
	if (messages[numMessages-1].getFight() == -1)
	{
		return false;
	}
	return true;
}


int NPC::getGame()
{
	return game;
}


void NPC::drawMessage(std::string s)
{
	sf::Text title(resizeMessage(s), *window->getFont());
	title.setCharacterSize(25);
	title.setColor(window->getBackground()->getTextColour());
	sf::FloatRect titleRect = title.getLocalBounds();
	sf::Vector2f center = window->getCenter();
	title.setPosition(messageLocation.x + window->getOffset(), messageLocation.y);
	window->draw(title);
}